<?php
namespace WDB\Query\Element;
use WDB,
    WDB\Exception;
/**
 * Table join holding two table sources.
 *
 * @author Richard Ejem <richard(at)ejem.cz>
 * @package
 */
final class Join extends \WDB\BaseObject implements iTableSource
{
    const INNER = 1;
    const OUTER = 2;
    const LEFT  = 3;
    const RIGHT = 4;

    /**no join condition (full cartesian product)*/
    const CARTESIAN = 1;
    /**natural join (join using all columns with same names in both tables)*/
    const NATURAL = 2;



    /**@var iTableSource*/
    private $a;
    /**@var iTableSource*/
    private $b;
    /**@var iCondition|Using|int*/
    private $condition;
    /**@var int*/
    private $type;

    /**
     *
     * @param iTableSource|string left side of join operator
     * @param iTableSource|string right side of join operator
     * @param iCondition|JoinUsing|int condition or using clause (int is enumeration of Join::[CARTESIAN|NATURAL])
     * @param int type (Enumeration of Join::[INNER|OUTER|LEFT|RIGHT])
     */
    public function __construct($a, $b, $condition = self::NATURAL, $type = self::INNER)
    {
        $this->setTableA($a);
        $this->setTableB($b);
        $this->setCondition($condition);
        $this->setType($type);
    }

    /**
     * Set join type (Enumeration of Join::[INNER|OUTER|LEFT|RIGHT])
     *
     * @param int type
     * @return Join fluent interface
     */
    public function setType($type)
    {
        $this->type = min(4, max(1, intval($type)));
        return $this;
    }

    /**
     * Get join type (Enumeration of Join::[INNER|OUTER|LEFT|RIGHT])
     *
     * @return int
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Set left side of join operator
     *
     * @param iTableSource|string
     * @return Join  fluent interface
     */
    public function setTableA($a)
    {
        if (is_string($a))
        {
            $a = new TableIdentifier($a);
        }
        elseif (!$a instanceof iTableSource)
        {
            throw new Exception\BadArgument("Table source must be a string or an iTableSource object");
        }
        $this->a = $a;
        return $this;
    }

    /**
     * Get left side of join operator
     *
     * @return iTableSource
     */
    public function getTableA()
    {
        return $this->a;
    }

    /**
     * Set right side of join operator
     *
     * @param iTableSource|string
     * @return Join  fluent interface
     */
    public function setTableB($b)
    {
        if (is_string($b))
        {
            $b = new TableIdentifier($b);
        }
        elseif (!$b instanceof iTableSource)
        {
            throw new Exception\BadArgument("Table source must be a string or an iTableSource object");
        }
        $this->b = $b;
        return $this;
    }

    /**
     * Get right side of join operator
     *
     * @return iTableSource
     */
    public function getTableB()
    {
        return $this->b;
    }

    /**
     * Set join condition/using/natural/cartesian
     *
     * @param iCondition|JoinUsing|int condition or using clause (int is enumeration of Join::[CARTESIAN|NATURAL])
     * @return Join fluent interface
     */
    public function setCondition($condition)
    {
        if (!$condition instanceof iCondition && !$condition instanceof JoinUsing && (!is_int($condition) || $condition < 1 || $condition > 2))
        {
            throw new Exception\BadArgument("join condition must be one of iCondition, JoinUsing or [1,2]");
        }
        $this->condition = $condition;
        return $this;
    }

    /**
     * Get join condition/using/natural/cartesian
     *
     * @return iCondition|JoinUsing|int condition or using clause (int is enumeration of Join::[CARTESIAN|NATURAL])
     */
    public function getCondition()
    {
        return $this->condition;
    }

    /**
     * Factory method preparing USING clause.
     *
     * @param string|ColumnIdentifier $col1,...
     * @return JoinUsing
     */
    public static function using()
    {
        return new JoinUsing(func_get_args());
    }

    public function toSQL(WDB\SQLDriver\iSQLDriver $driver)
    {
        return $driver->tosql_tableJoin($this);
    }

    public function alias($alias)
    {
        return AliasedTableSource::create($this, $alias);
    }
}
